using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Reflection;

namespace NWrapper.Entities
{
	/// <summary>
	/// A base class for data entities. Domain objects (entities) can inherit from this class
	/// to provide some convenient functionality.
	/// </summary>
	public abstract class EntityBase : IComparable
	{
		#region Constructors
		/// <summary>
		/// Default constructor.
		/// </summary>
		protected EntityBase()
		{
		}
		#endregion

		#region IComparable
		/// <summary>
		/// Compares two objects using reference equality. Syntatically equivalent to
		/// <c>this == obj</c>.
		/// </summary>
		/// <param name="obj">The object to compare to.</param>
		/// <returns><c>0</c> if the objects are equal, <c>-1</c> if <paramref name="obj"/> is null, 
		/// <c>1</c> if the objects are not equal.</returns>
		public int CompareTo(object obj)
		{
			return (this == obj ? 0 : (obj == null ? -1 : 1));
		}
		#endregion

		#region System.Object
		/// <summary>
		/// Compares two objects. This method calls <see cref="CompareTo"/> internally,
		/// so comparison functions are based on comparison functions of that method.
		/// </summary>
		/// <param name="obj">The object to compare to.</param>
		/// <returns>True if the objects are equal, false otherwise.</returns>
		public override bool Equals(object obj)
		{
			return (this.CompareTo(obj) == 0);
		}
		/// <summary>
		/// Returns a hash code for this entity. This method calls does not implement
		/// any specific functionality; rather, the base
		/// <see cref="GetHashCode"/> method is called.
		/// </summary>
		/// <returns>A numeric hash code.</returns>
		public override int GetHashCode()
		{
			return base.GetHashCode();
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// Gets a collection of key/value pairs that reflect the names and values of
		/// all properties on this object.
		/// </summary>
		/// <returns>A <see cref="HybridDictionary"/> containing the key/value pairs.</returns>
		public IDictionary GetProperties()
		{
			PropertyInfo[] properties = this.GetType().GetProperties();
			HybridDictionary results = new HybridDictionary(properties.Length);

			foreach (PropertyInfo propInfo in properties)
			{
				results.Add(propInfo.Name, propInfo.GetValue(this, null));
			}

			return results;
		}
		/// <summary>
		/// Sets the properties of this object based on a provided collection of key/value pairs.
		/// </summary>
		/// <param name="properties">The key/value pairs to set.</param>
		/// <remarks>Keys that do not have corresponding properties of the same name are ignored.</remarks>
		public void SetProperties(IDictionary properties)
		{
			foreach (object key in properties.Keys)
			{
				PropertyInfo propInfo = this.GetType().GetProperty(key.ToString());
				if (propInfo == null)
					continue;

				Type propertyType = propInfo.PropertyType;
				if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
				{
					propertyType = Nullable.GetUnderlyingType(propertyType);
				}

				object propValue = null;
				if (properties[key] != null)
				{
					switch (propertyType.FullName.ToLower())
					{
						case "system.guid":
							propValue = new Guid((string)properties[key]);
							break;
						default:
							propValue = Convert.ChangeType(properties[key], propertyType);
							break;
					}
				}
				propInfo.SetValue(this, propValue, null);
			}
		}
		#endregion
	}
}
